package com.py.custom.ability.kafka.utils;

import com.py.custom.ability.kafka.common.exception.IllegalConfigurationException;
import org.springframework.boot.context.properties.bind.BindHandler;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.bind.PropertySourcesPlaceholdersResolver;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.core.env.Environment;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class MultiBinder<P> {

    /**
     * springboot 环境
     */
    private final Environment environment;

    /**
     * 前缀
     */
    private final String configPrefix;

    /**
     * 检查参数
     */
    private final PropertiesChecker<P> checker;

    /**
     * 数据属性
     */
    private final Iterable<ConfigurationPropertySource> sources;

    /**
     * 是否多配置
     */
    private boolean hasMultiProperties = true;


    public MultiBinder(Environment environment, String configPrefix, PropertiesChecker<P> checker) {
        this.environment = environment;
        this.configPrefix = configPrefix;
        this.checker = checker;
        this.sources = ConfigurationPropertySources.get(environment);
    }

    public MultiBinder(Environment environment, String configPrefix, PropertiesChecker<P> checker,
                       Iterable<ConfigurationPropertySource> sources) {
        this.environment = environment;
        this.configPrefix = configPrefix;
        this.checker = checker;
        this.sources = sources;
    }

    public List<P> bind(Class<P> target) {
        return bind(target, null);
    }

    public List<P> bind(Class<P> target, BindHandler bindHandler) {
        List<P> propertiesList = new ArrayList<>();

        Binder binder = new Binder(sources, new PropertySourcesPlaceholdersResolver(environment));
        // single
        P singleProperties = binder.bind(configPrefix, Bindable.of(target), bindHandler)
                .orElse(null);

        // multi
        List<P> multiProperties = binder.bind(configPrefix, Bindable.listOf(target), bindHandler)
                .orElse(Collections.emptyList());

        // single && multi 都没有获取到配置，直接返回空
        if (null == singleProperties && CollectionUtils.isEmpty(multiProperties)) {
            return Collections.emptyList();
        }

        // single && multi 不能同时存在
        if (null != singleProperties &&
                !CollectionUtils.isEmpty(multiProperties)) {
            throw new IllegalConfigurationException("[custom] [MultiBinder] 同时存在单个和多个bean实例配置！");
        }


        if (null != singleProperties) {
            validate(singleProperties);
            propertiesList.add(singleProperties);
            this.hasMultiProperties = false;
        }

        if (!CollectionUtils.isEmpty(multiProperties)) {
            for (P p : multiProperties) {
                validate(p);
                propertiesList.add(p);
            }
        }

        return Collections.unmodifiableList(propertiesList);
    }

    private void validate(P toBeCheck) {
        if (checker == null || checker.check(toBeCheck)) {
            return;
        }

        throw new IllegalConfigurationException("配置有问题，请检查!" + toBeCheck);
    }

    public interface PropertiesChecker<T> {

        /**
         * 校验获取到的配置是否有效
         *
         * @param toBeCheckedProperties
         * @return true-有效， 或者内部抛出异常代表配置无效不让启动
         */
        boolean check(T toBeCheckedProperties);
    }
}
